/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package kpython.backend.semantic;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;

/**
 * Corresponde a um ambiente. Sempre que estamos dentro da defini��o
 * de uma fun��o, ou numa instru��o de selec��o e repeti��o
 * deve ser criado um novo scope. 
 * 
 * @author Dinis
 */
public class Scope {

    /**
     * Construtor por defeito
     */
    public Scope(String id) {
        this.id = id;
    }

    /**
     * Construtor
     * 
     * @param parent
     */
    public Scope(Scope parent, String id) {
        this.parent = parent;
        this.id = id;
    }

    /**
     * Devolve o pr�ximo filho
     * 
     * @return
     */
    public Scope nextChild(String id) {

        Scope nextC;
        if (next >= children.size()) {

            nextC = new Scope(this, id);
            children.add(nextC);
        } else {
            nextC = (Scope) children.get(next);
        }
        next++;
        return nextC;
    }
    

    /**
     * Insere um novo registo no Scope
     * 
     * @param key
     * @param item
     */
    public void put(Symbol key, Record item) {
        item.setScopeId(this.idCount++);
        records.put(key, item);
    }

    /**
     * 
     * 
     * @param key
     * @return
     */
    public Record lookup(Symbol key) {
        if (records.containsKey(key)) {
            return records.get(key);
        } else {
            if (parent == null) {
                return null;
            } else {
                return parent.lookup(key);
            }
        }
    }

    public Record currentLookup(Symbol key) {
        if (records.containsKey(key)) {
            return records.get(key);
        }
        return null;
    }

    public Scope getNextChild(boolean fromFirst)
    {
        if( fromFirst )
            this.actual = 0;
        
        if(actual >=  children.size()) return null;
        return children.get(actual);
    }
    
    
    /**
     * Reiniciliza o scope e os seus descendentes
     */
    public void resetScope() {
        next = 0;
        for (int i = 0; i < children.size(); i++) {
            children.get(i).resetScope();
        }
    }
    
    public int getSymbolIndex(Symbol key) {    
        Record s = records.get(key);
        if (s != null) return s.getScopeId();
        return -1;
    }
    
    public Scope getScope(String id) {
        if(this.id.equals(id)) {
            return this;
        }
        Scope child;
        for (Scope scope : children) {
            if ((child = scope.getScope(id)) != null)
                return child;                     
        }
        return null;
    }

    public void printScope(int level) {
        String padding = new String();
        System.out.println(id+":");
        for (int i = 0; i < level; i++) {
            padding += "-";
        }
        for (Record rec : records.values()) {
            System.out.println(padding + rec.toString());
        }
        for (Scope scope : children) {
            scope.printScope(++level);
        }
    }
    /**
     * scope(child)  seguinte
     */
    private int next = 0;
    /**
     * scope (child) actual
     */
    private int actual = 0;
    /**
     * Pai deste scope
     */
    private Scope parent;
    
    /**
     *  Identificador do scope
     *  
     */
    private String id;
    
    private int idCount = 0;

    /**
     * retorna o pai deste scope
     * 
     * @return parent
     */
    public Scope getParent() {
        return this.parent;
    }
    
    public String getId() {
        return this.id;
    }
    
    public int getNumberSymbols() {
    	return records.size();
    }
    
    public String toString() {
    	return id;
    }
    
    /**
     * Lista de scopes descendentes
     */
    private List<Scope> children = new ArrayList<Scope>();
    /**
     * Table com o registo dos s�mbolos pertencentes a este scope
     */
    private HashMap<Symbol, Record> records = new HashMap<Symbol, Record>();
}
